/*
 * Map.cpp
 *
 *  Created on: Oct 11, 2012
 *      Author: travis
 *	
 *	Main map class
 */

#ifdef _WIN32 
#include <windows.h> 
#endif

#include "Map.h"
#include "scene/Cursor.h"

namespace Map
{

Map::Map(Timer *_fps)
{
	// defaultni nastaveni mapy
	fps = _fps;

	Name = "myMap";
	MapSize = 257;

	OcMainBox = NULL;
	Octree::setRootNesting(CULLING_DIV);

	// create new water surface
	water = new Water(1.8, (MapSize - 1) * 1.0,
			(int) pow(2.0, (int) CULLING_DIV));

	// create new terrain
	ter = new Terrain::Terrain(water, MapSize, 1.0f);

	// create sky
	sky = new Sky(10.0, 200.0);

	showAll(); // set all parts of map visible

	// create model pool and load models
	mPool = new ModelPool(); // setup model pool
	mPool->loadModel("house", "");
	//mPool->loadModel("gatherer","");

	// initialize shadow map
	shadows = new glShadow(MapSize, ter->STEP);

	//init reflection
	reflection = new glReflection(water);

	//init refraction
	refraction = new glRefraction(water);

	cursor = new RTSCursor::Cursor(this);

	//Creating instance of pathfinder
	pathfinder = new PathFinder(ter);
	pathfinder->updateMapData(257);

	entities = new Objects::Objects(mPool, pathfinder, OcMainBox, ter);

	bBoxVisible = false;
	octreeBox = false;
}

Map::~Map()
{
	// stop thread
	ThrPool::quitEntityThread();

	delete (Shader::sPool);
	delete (ter);
	delete (water);
	delete (mPool);
	delete (shadows);
	delete (reflection);
	delete (refraction);
	delete (pathfinder);
}

// called from thread in RTS!!!!
void Map::control(SDL_Event *event, float t)
{
	cursor->control(event, t);

	//entities->control(t);
}
//----------------------------------

void Map::drawAll()
{
	int mx, my; // mouse
	// minimum point of visible terrain
	// used for culling water surface and sky
	// if there is no visible part of water -> don't draw water,
	// sky, reflection nither refraction
	float minVisibleTerY = 0.0; // minimum point of visible terrain

	// textures
	GLuint refractionTexture;
	GLuint refTexture;

	SDL_GetMouseState(&mx, &my);

	// create shadow map
	shadows->glShadowBegin();
	//if (terVisible) Octree->drawVisible(0, SHADER_SHADOW, octreeBox);
	if (entVisible)
		entities->drawEntities(SHADER_SHADOW);
	GLuint depthTexture = shadows->glShadowEnd();

	// create refraction texture, mesh of terrain under water
	refraction->glRefractionBegin();
	minVisibleTerY = OcMainBox->drawVisible(0, SHADER_CLIP_ABOVE, octreeBox, false,
			true, 0, 0);

	refractionTexture = refraction->glRefractionEnd();

	// create refection texture only if water is visible
	if ((minVisibleTerY <= water->getHigh()) && (watVisible))
	{
		reflection->glReflectionBegin();
		//if (terVisible) Octree->drawVisible(depthTexture, SHADER_CLIP_UNDER, octreeBox);
		if (skyVisible)
			sky->draw(); // draw sky
		if (entVisible)
			entities->drawEntities(SHADER_REFLECTION);
		refTexture = reflection->glReflectionEnd();
	}
	else
		refTexture = 0;
	// draw from Camera::camera

	// draw only mesh above water

	OcMainBox->drawVisible(depthTexture, SHADER_CLIP_UNDER, octreeBox, watVisible,
			terVisible, refTexture, refractionTexture);

	// draw water with reflection & refraction texture
	/*
	 if ((watVisible) && (minVisibleTerY <= water->getHigh())) 
	 water->draw(refTexture, refractionTexture);	*/

	// update terrain cursor in cursor
	cursor->updateOGLPos(mx, my, UPDATE_TER_CUR);

	// draw entities (only mooving objects)
	// rest of entities (static) are managed by octree
	if (entVisible)
		entities->drawEntities(SHADER_NORMAL);

	// update entity cursor
	cursor->updateOGLPos(mx, my, UPDATE_ENT_CUR);

	// draw cursor
	cursor->draw();

	// draw bounding boxes if they are visible 
	if (bBoxVisible)
		entities->drawEntBBox();
}

void Map::clearMap()
{
	ter->clearTerrain();
	entities->deleteAllEnt();
}

void Map::generateMap()
{
	// stop control thread
	if (ThrPool::isRunning())
		ThrPool::quitEntityThread();

	// delete old octree
	if (OcMainBox != NULL)
		delete (OcMainBox);

	// delete old entities
	entities->deleteAllEnt();

	// generate terrain until Max high value is under 5.1
	while (ter->getLocalMinMax(0,0,ter->get_size()).y < 5.1f)
		ter->generate_terrain(70); // generuje teren 12

	// create new octree for new terrain
	OcMainBox = new Octree::Box(1, 1, ter->get_size(), CULLING_DIV, ter->STEP, ter,
				water);

	// set counter to 0
	World::countBoxes = 0;

	// set new octree in entity
	entities->setOctree(OcMainBox);

	// update walkability map data after each regen of terrain;
	pathfinder->updateMapData(water->getHigh());

	glm::vec3 buf;
	srand(time(NULL));
	int maxTry = 1000;

	// insert main store house on position 0
	int bx = 0;
	int by = 0;
	float r;

	do // not under water!!!
	{
		bx = rand() % (MapSize - 10) + 5;
		by = rand() % (MapSize - 10) + 5;
		maxTry -= 1;
	} while ((ter->get_value(bx, by) < 5.0) || (ter->get_value(bx, by) > 9.0) || (maxTry > 0));

	r = rand() % 360;

	buf.x = bx * ter->STEP;
	buf.z = by * ter->STEP;
	buf.y = ter->getInterYVal(glm::vec2(buf.x, buf.z));

	std::string nbuf = "Main house";

	std::string mbuf = "storeHouse";

	entities->insertBuilding(buf, r, nbuf, mbuf, 100.0, OBJ_BBUILDER,
			STATUS_READY);

	// set Camera::camera lookAt
	if (Camera::camera != NULL)
		Camera::camera->lookAt(glm::vec2(buf.x, buf.z),
				Engine::cameraSettings.MaxZoom);
	//-------------------------------------------------

	//--------------------------------------------------------------------------
	// generovani stromu na nahodne pozici
	for (int i = 0; i < 300; i++)
	{
		float age;

		maxTry = 100;

		do // not under water!!!
		{
			bx = rand() % (MapSize - 10) + 5;
			by = rand() % (MapSize - 10) + 5;
			maxTry -= 1;
		} while ((ter->get_value(bx, by) < 1.9) || (ter->get_value(bx, by) > 12.0) || (maxTry > 0));

		r = rand() % 360;
		age = rand() % 50 + 50.0;

		buf.x = bx * ter->STEP;
		buf.z = by * ter->STEP;
		buf.y = ter->getInterYVal(glm::vec2(buf.x, buf.z));

		std::stringstream sn; //create a stringstream
		sn << i; //add number to the stream
		std::string nbuf = "Tree " + sn.str();

		std::stringstream ss; //create a stringstream
		ss << (rand() % 6 + 1); //add number to the stream
		std::string mbuf = "tree0" + ss.str();
		//insertEntity(OBJ_TREE, buf, r, nbuf, mbuf);
		entities->insertTree(buf, r, nbuf, mbuf, age);

		//std::cout << " " << r << " " << nbuf << " " << mbuf << std::endl;
	}

	for (int i = 0; i < 40; i++)
	{
		maxTry = 100;

		do // not under water!!!
		{
			bx = rand() % (MapSize - 10) + 5;
			by = rand() % (MapSize - 10) + 5;
			maxTry -= 1;
		} while ((ter->get_value(bx, by) < 1.9) || (ter->get_value(bx, by) > 7.0) || (maxTry > 0));

		r = rand() % 360;

		buf.x = bx * ter->STEP;
		buf.z = by * ter->STEP;
		buf.y = ter->getInterYVal(glm::vec2(buf.x, buf.z));

		std::stringstream ss; //create a stringstream
		ss << i; //add number to the stream
		std::string nbuf = "Deer" + ss.str();

		std::string mbuf = "deer01";
		entities->insertDeer(buf, r, nbuf, mbuf);
		//std::cout << " " << r << " " << nbuf << " " << mbuf << std::endl;
	}

	// start control THREAD
	ThrPool::startControlThread(entities);
}

int Map::getSize()
{
	return MapSize;
}

void Map::setSize(int s)
{
	MapSize = s;
}

std::string Map::getName()
{
	return Name;
}

void Map::setName(std::string N)
{
	Name = N;
}

void Map::showTerrain()
{
	terVisible = true;
}

void Map::hideTerrain()
{
	terVisible = false;
}

void Map::showWater()
{
	watVisible = true;
}

void Map::hideWater()
{
	watVisible = false;
}

void Map::showAll()
{
	terVisible = true;
	watVisible = true;
	entVisible = true;
	skyVisible = true;
}

void Map::hideAll()
{
	terVisible = false;
	watVisible = false;
	entVisible = false;
	skyVisible = false;
}

bool Map::isWaterVisible()
{
	return watVisible;
}

bool Map::isTerrainVisible()
{
	return terVisible;
}

void Map::showEntities()
{
	entVisible = true;
}

void Map::hideEntities()
{
	entVisible = false;
}

bool Map::areEntitiesVisible()
{
	return entVisible;
}

void Map::showSky()
{
	skyVisible = true;
}

void Map::hideSky()
{
	skyVisible = false;
}

ModelPool*& Map::getPool()
{
	return mPool;
}

void Map::setPool(ModelPool*& pool)
{
	mPool = pool;
}

PathFinder*& Map::getPathfinder()
{
	return pathfinder;
}

void Map::setPathfinder(PathFinder*& pathfinder)
{
	this->pathfinder = pathfinder;
}

glReflection*& Map::getReflection()
{
	return reflection;
}

void Map::setReflection(glReflection*& reflection)
{
	this->reflection = reflection;
}

glRefraction*& Map::getRefraction()
{
	return refraction;
}

void Map::setRefraction(glRefraction*& refraction)
{
	this->refraction = refraction;
}

glShadow*& Map::getShadows()
{
	return shadows;
}

void Map::setShadows(glShadow*& shadows)
{
	this->shadows = shadows;
}

Sky*& Map::getSky()
{
	return sky;
}

void Map::setSky(Sky*& sky)
{
	this->sky = sky;
}

Terrain::Terrain*& Map::getTer()
{
	return ter;
}

void Map::setTer(Terrain::Terrain*& ter)
{
	this->ter = ter;
}

Water*& Map::getWater()
{
	return water;
}

void Map::setWater(Water*& water)
{
	this->water = water;
}

RTSCursor::Cursor*& Map::getCursor()
{
	return cursor;
}

void Map::setCursor(RTSCursor::Cursor*& cursor)
{
	this->cursor = cursor;
}

bool Map::isBoxVisible() const
{
	return bBoxVisible;
}

void Map::setBoxVisible(bool boxVisible)
{
	bBoxVisible = boxVisible;
}

Octree::Box*& Map::getOctree()
{
	return OcMainBox;
}

void Map::setOctree(Octree::Box*& octree)
{
	OcMainBox = octree;
}

bool Map::isOctreeBox()
{
	return octreeBox;
}

void Map::setOctreeBox(bool octreeBox)
{
	this->octreeBox = octreeBox;
}

Objects::Objects* Map::getEntities()
{
	return entities;
}

}
